package org.xue.mybatisplus.controller;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.*;
import org.redisson.codec.FstCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.xue.mybatisplus.entity.OrderPojo;
import org.xue.mybatisplus.entity.User;

import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class TestController {


    @Autowired
    private RedissonClient redisson;

    private static final String USER_KEY = "user:key:";
    /**
     * 读写锁（ReadWriteLock） 测试1
     * 读锁：共享锁，所有线程共享读锁
     * 写锁: 独占锁,只能单独线程占用，执行完才能执行其他“读写锁”。
     */
    @ResponseBody
    @GetMapping("/test/readWriteLock/toRead")
    public String toReadLock() {
        //获取 锁
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("readWriteLock");
        RLock rLock = readWriteLock.readLock();
        //上 读锁
        rLock.lock();
        String writeValue = "";
        try {
            log.info("【读锁-{}】加锁成功，读数据...", Thread.currentThread()
                    .getId());

            RBucket<Object> jierong = redisson.getBucket("jierong");
//            jierong.set(new User(1,"1",1,"2"));
            writeValue= (String) jierong.get();
        }finally {
            log.info("【读锁-{}】解锁成功,uuid={}", Thread.currentThread()
                    .getId(),writeValue);
            //解锁
            rLock.unlock();
        }
        return writeValue;
    }


    @RequestMapping(value = "/test01")
    public String toRead() throws InterruptedException {

        RLock lock = redisson.getFairLock("lock_jierong");
        String threadName = Thread.currentThread().getName();
//        lock.lock();
//        lock.unlock();

        // 尝试获取锁，加锁成功后启动看门狗；非阻塞，失败立马返回；注意释放锁时要判断是否存在及是否被当前线程保持

            boolean tryLock = lock.tryLock(5,TimeUnit.SECONDS);
            if (!tryLock){
                System.out.println("进入");
                return "加锁失败，请稍后重试！";
            }




        // 指定时间内尝试获取锁，失败立即返回；成功后设置有效时间为指定值，无看门狗
//        boolean tryLock1 = lock.tryLock(5, 10, TimeUnit.SECONDS);
//        if (!tryLock1){
//            System.out.println("进入1");
//           return "失败";
//        }


        // 在指定时间内尝试获取锁，失败立即返回；有看门狗
//        boolean tryLock2 = lock.tryLock(5, TimeUnit.SECONDS);
//        if (!tryLock2){
//            System.out.println("进入2");
//            return "加锁失败，请稍后重试2！";
//        }

        return "我要吃杰荣";
    }

    @RequestMapping(value = "/test03")
    public String toRead222() throws InterruptedException {

        RLock fairLock = redisson.getFairLock("jierong");
        boolean b = fairLock.tryLock(100, 100, TimeUnit.SECONDS);
        System.out.println(b);
        if (b==true){
            System.out.println("枷锁成功");
        }else {
            System.out.println("加锁失败");
        }
        return "111";
    }




    @RequestMapping(value = "test02")
    public String toRead2() throws InterruptedException {

        RLock lock = redisson.getFairLock("lock_jierong");
        String threadName = Thread.currentThread().getName();
//        lock.lock();
//        lock.unlock();

        // 尝试获取锁，加锁成功后启动看门狗；非阻塞，失败立马返回；注意释放锁时要判断是否存在及是否被当前线程保持

        // 在指定时间内尝试获取锁，失败立即返回；有看门狗
        boolean tryLock2 = lock.tryLock(6, TimeUnit.SECONDS);
        if (!tryLock2){
            System.out.println("进入2");
            return "加锁失败，请稍后重试2！";
        }
        return "测试";
    }


    @GetMapping("/testFairLock")
//    @ApiOperation("公平锁")
    public String testFairLock(@RequestParam Long goodsId) {
        //获取锁对象
        RLock fairLock = redisson.getFairLock("fairLock_" + goodsId);
        String threadName = Thread.currentThread().getName();
        try {
            Boolean b = fairLock.tryLock(10,TimeUnit.SECONDS);
            if (b){
                //获取到锁对象
                log.info("{}：获得锁，开始执行业务", threadName);
                //模拟业务40秒
                TimeUnit.SECONDS.sleep(40);
                log.info("{}：执行结束", threadName);
                return threadName+"成功";
            }


        } catch (Exception e) {
            log.error("testFairLock exception:", e);
            return threadName+"获取锁失败";
        } finally {
            boolean locked = fairLock.isLocked();
            boolean heldByCurrentThread = fairLock.isHeldByCurrentThread();
            log.info("{}：获取锁状态：{} 是否当前线程保留：{}", threadName, locked, heldByCurrentThread);
            if (locked && heldByCurrentThread) {
                fairLock.unlock();
                log.info("{}：释放锁成功", threadName);
            } else {
                log.info("{}：未获得锁不用释放", threadName);
            }

        }
        return "失败";
    }



    @GetMapping("/testMultLock")
    public String testMultLock(@RequestParam Long id) {
        //联锁联锁（RedissonMultiLock）对象可以将多个RLock对象关联为一个联锁，实现加锁和解锁功能。每个RLock对象实例可以来自于不同的Redisson实例
        String threadName = Thread.currentThread().getName();
        //获取三个锁（锁就是处理某业务固定的锁）对象
        RLock one = redisson.getLock("one_" + id);
        RLock two = redisson.getLock("two_" + id);
        RLock three = redisson.getLock("three_" + id);
        RedissonMultiLock multiLock = new RedissonMultiLock(one, two, three);
        try {
            // 所有的锁都上锁成功才算成功
            boolean b = multiLock.tryLock(10, TimeUnit.SECONDS);
            log.info("{}：获得锁，开始执行业务", threadName);
            TimeUnit.SECONDS.sleep(15);
            log.info("{}：执行结束", threadName);
            return "成功";
        } catch (Exception e) {
            log.error("testMultLock exception:", e);
            return "异常";
        } finally {
            // 注意：不能使用isLocked()和isHeldByCurrentThread()方法，会抛出UnsupportedOperationException异常
            multiLock.unlock();
            log.info("{}：释放锁成功", threadName);
        }
    }

    @GetMapping("/testRedLock")
//    @ApiOperation("红锁")
    public String testRedLock(@RequestParam Long id) {
        String threadName = Thread.currentThread().getName();
        RLock one = redisson.getLock("one_" + id);
        RLock two = redisson.getLock("two_" + id);
        RLock three = redisson.getLock("three_" + id);
        RedissonMultiLock redLock = new RedissonRedLock(one, two, three);
        try {
            redLock.lock();
            log.info("{}：获得锁，开始执行业务", threadName);
            TimeUnit.SECONDS.sleep(2);
            log.info("{}：执行结束", threadName);
            return "321";
        } catch (Exception e) {
            log.error("testRedLock exception:", e);
            return "123";
        } finally {
            // 注意：不能使用isLocked()和isHeldByCurrentThread()方法，会抛出UnsupportedOperationException异常
            redLock.unlock();
            log.info("{}：释放锁成功", threadName);
        }
    }


    @RequestMapping(value = "/tong")
    public void tong(){
        RGeo<String> geo = redisson.getGeo("test");
        geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"),
                new GeoEntry(15.087269, 37.502669, "Catania"));
        geo.addAsync(37.618423, 55.751244, "Moscow");
        Double distance = geo.dist("Palermo", "Catania", GeoUnit.METERS);
        geo.hashAsync("Palermo", "Catania");

        Map<String, GeoPosition> positions = geo.pos("test2", "Palermo", "test3", "Catania", "test1");
        List<String> cities = geo.radius(15, 37, 200, GeoUnit.KILOMETERS);
        Map<String, GeoPosition> citiesWithPositions = geo.radiusWithPosition(15, 37, 200, GeoUnit.KILOMETERS);


        RBucket<Object> jierong = redisson.getBucket("test");
        System.out.println(jierong.get());

    }



    @RequestMapping("/testadd")
    public void test33() throws InterruptedException {


        List<OrderPojo> orderPojos = new ArrayList<>();
        for (int i=0;i<5;i++){
            //叫车
            OrderPojo order = new OrderPojo(i+"xx",3,3,101);
            orderPojos.add(order);
        }

        for (OrderPojo order : orderPojos) {
            RMap<Object,Object> rMap = redisson.getMap(order.getOrderId());
            rMap.put("orderId",order.getOrderId());
            rMap.put("passengerId",order.getPassengerId());
            rMap.put("driverId",order.getDriverId());
            rMap.put("state",order.getState());
            RSet<Object> rSet = redisson.getSet("pendset-order");
            rSet.add(order.getOrderId());
            System.out.println("发布订单成功，等待司机接单");
        }




    }




    @RequestMapping("/testrob")
    public void test22(){

        OrderPojo order = new OrderPojo("1",2,3,101);

        RSet<Object> set = redisson.getSet("gainset-order");
        set.add(order.getOrderId());
        boolean b = redisson.getSet("pendset-order").remove(order.getOrderId());

        if (b) {
            System.out.println("被司机"+Thread.currentThread().getName()+"抢到");
        }else {
            System.out.println("司机"+Thread.currentThread().getName()+"没抢到");
        }



    }


    @RequestMapping("/jierong")
    public String testrod(){
        Random random = new Random();



        for (int x=0;x<50;x++){
            int i = random.nextInt(25);
            int driverId = x;
//            String orderId = "1xx";
            //叫车
            OrderPojo order = new OrderPojo(i+"xx",3,3,101);
//            System.out.println(x+"xx");
//            System.out.println(order.getOrderId());
            RBucket<Object> bucket = redisson.getBucket(order.getOrderId());

//            if (bucket==null){
//                System.out.println("司机"+driverId+"没抢到"+order.getOrderId());
//                continue;
//            }

            long t1=System.currentTimeMillis();
            System.out.println(t1);

            redisson.getScoredSortedSet("xx").addScore("杰荣",t1);

            try {
                String s = bucket.get().toString();
            }catch (NullPointerException e){
                continue;
            }

            boolean b = bucket.delete();
            if (!b) System.out.println("失败");
            //尝试添加到行程中订单池
            boolean isrob = redisson.getSet("isrob").add(order.getOrderId());
            if (!isrob){
                System.out.println("被抢了");
                continue;
//                return "被抢";
            }
            System.out.println("司机"+driverId+"抢到"+order.getOrderId());
        }



        //撮合司机订单  生成新订单
//        OrderPojo orderPojo = new OrderPojo(orderId,1,driverId,101);
        return "sb";
    }






}
